/**
 * 
 */
package info.niwota.ziplock;

import info.niwota.ziplock.FolderContentProvider.Columns;
import info.niwota.ziplock.zip.CryptoUtils;

import java.io.File;
import java.security.Key;

import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import com.admob.android.ads.AdView;

/**
 * @author qiangli
 * 
 */
public class BaseActivity extends ListActivity {
	private class InitTask extends AsyncTask<Void, Void, Void> {

		@Override
		protected Void doInBackground(Void... params) {
			try {
				//
				autoImport();
				//
				cursor = getContentResolver().query(
						FolderContentProvider.CONTENT_URI, null, null, null,
						null);
				startManagingCursor(cursor);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onCancelled() {
			dismissDialog(DIALOG_PROGRESS);
		}

		@Override
		protected void onPostExecute(Void result) {
			try {
				//
				setAdapter();
			} finally {
				dismissDialog(DIALOG_PROGRESS);
			}
		}

		@Override
		protected void onPreExecute() {
			showDialog(DIALOG_PROGRESS);
		}

	}

	private class LockTask extends AsyncTask<Void, Void, Integer> {
		private static final int WHAT_FAIL = 2;
		private static final int WHAT_OK = 1;

		private int action;

		private Exception error;

		private int pos;

		public LockTask(int action, int pos) {
			this.action = action;
			this.pos = pos;
		}

		@Override
		protected Integer doInBackground(Void... params) {
			Integer rc;
			try {
				if (action == 1) {
					lockZip(pos);
				} else {
					unlockZip(pos);
				}
				rc = (WHAT_OK);
			} catch (Exception e) {
				rc = (WHAT_FAIL);
				error = e;
			}
			return rc;
		}

		@Override
		protected void onCancelled() {
			try {
				dismissDialog(DIALOG_PROGRESS);
			} catch (Exception e) {
			}
		}

		@Override
		protected void onPostExecute(Integer result) {
			try {
				dismissDialog(DIALOG_PROGRESS);
			} catch (Exception e) {
			}
			if (result == null) {
				return;
			}
			switch (result) {
			case WHAT_OK: {
				refresh();
				break;
			}
			case WHAT_FAIL: {
				showError(error);
				break;
			}
			}
		}

		@Override
		protected void onPreExecute() {
			showDialog(DIALOG_PROGRESS);
		}
	}

	private static final int DIALOG_DELETE = 6;

	private static final int DIALOG_HELP = 1;

	private static final int DIALOG_LOGIN = 2;

	private static final int DIALOG_PROGRESS = 5;

	private static final int DIALOG_PWD = 3;

	private static final int DIALOG_SETPWD = 4;

	private static final int RC_IMPORT = 3;

	private static final int RC_MULTI_SELECT = 1;

	private static final int RC_SETTINGS = 2;

	private static final String SAVED_KEY = "saved.key";

	private static final String TAG = "MainActivity";

	private Cursor cursor;

	private Dialog diaDelete;
	private MenuItem miChangePwd;
	private MenuItem miLogin;
	private MenuItem miLogout;

	protected MenuItem miQuit;

	private MenuItem miSetPwd;

	private int selPosition;

	private void autoImport() {
		if (Debug.DEBUG) {
			Log.d(TAG, "autoImport");
		}
		final File base = FolderContentProvider.ZIP_BASE;
		final File imp = FolderContentProvider.ZIP_IMPORT;
		//
		final File[] zips = imp.listFiles();
		if (zips == null || zips.length == 0) {
			return;
		}
		if (Debug.DEBUG) {
			Log.d(TAG, "autoImport " + zips.length);
		}

		for (File f : zips) {
			try {
				String n = f.getName();
				if (n.endsWith(".zip") == false) {
					continue; //
				}
				File dest = FileUtils.newZipFile(this, base, n);
				boolean b = f.renameTo(dest);
				if (!b) {
					if (Debug.DEBUG) {
						Log.d(TAG, "autoImport rename failed, copying..." + f);
					}
					FileUtils.copy(f, dest);
					f.delete();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void cleanupZipCache(String name) {
		try {
			File ca = new File(FileContentProvider.ROOT, name);
			if (!ca.exists()) {
				return;
			}
			FileUtils.deleteAll(ca);
			if (Debug.DEBUG) {
				Log.d(TAG, "cleanupZipCache: " + ca);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void clearCache() {
		try {
			// clear cache
			File cache = getCacheDir();
			if (Debug.DEBUG) {
				Log.d(TAG, "cache: " + cache);
			}
			FileUtils.deleteAll(cache);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/* pkg */void deleteZip() throws Exception {
		try {
			String p = getPath(selPosition);
			File file = new File(p);
			if (Debug.DEBUG) {
				Log.d(TAG, "delete file: " + file);
			}
			//
			cleanupZipCache(file.getName());
			//
			file.delete();
		} catch (Exception e) {
			throw e;
		} finally {
			refresh();
		}
	}

	private void doDelete(int pos) {
		this.selPosition = pos;
		showDialog(DIALOG_DELETE);
	}

	private void doFindZip() {
		Intent i = new Intent(this, ZipImportActivity.class);
		startActivityForResult(i, RC_IMPORT);
	}

	private void doLock(int pos) {
		LockTask task = new LockTask(1, pos);
		task.execute();
	}

	private void doLogin() {
		showDialog(DIALOG_LOGIN);
	}

	private void doLogout() {
		logout(false);
		//
		refresh();
	}

	private void doOpen(int position) {
		String p = getPath(position);
		Uri data = Uri.withAppendedPath(ZipContentProvider.CONTENT_URI, p
				.substring(1));
		String action = getIntent().getAction();
		if (Debug.DEBUG) {
			Log.d(TAG, "open: action: " + action);
		}
//		//
//		Class<?> clz = ZipFileListActivity.class;
//		switch (Preferences.getDefaultView(this)) {
//		case Preferences.VIEW_LIST:
//			clz = ZipFileListActivity.class;
//			break;
//		case Preferences.VIEW_THUMBNAILS:
//			clz = MultiSelectImageActivity.class;
//			break;
//		default:
//		}

		Intent i = new Intent(action, data, this, ViewsActivity.class);
		startActivityForResult(i, RC_MULTI_SELECT);
	}

	protected void doQuit() {
	}

	private void doSettings() {
		try {
			Intent i = new Intent(this, SettingsActivity.class);
			startActivityForResult(i, RC_SETTINGS);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doUnlock(int pos) throws Exception {
		LockTask task = new LockTask(2, pos);
		task.execute();
	}

	private String getName(int position) {
		Cursor c = cursor;
		c.moveToPosition(position);
		String name = c.getString(c.getColumnIndexOrThrow(Columns.NAME));
		if (Debug.DEBUG) {
			Log.d(TAG, "name: " + name);
		}
		return name;
	}

	private String getPath(int position) {
		Cursor c = cursor;
		c.moveToPosition(position);
		String name = c.getString(c.getColumnIndexOrThrow(Columns.NAME));
		String desc = c.getString(c.getColumnIndexOrThrow(Columns.DESCRIPTION));
		String uri = c.getString(c.getColumnIndexOrThrow(Columns.INTENT));

		Uri zipUri = Uri.parse(uri);
		String p = zipUri.getPath();
		if (Debug.DEBUG) {
			Log.d(TAG, "name: " + name + " desc: " + desc + " uri: " + uri
					+ " path: " + p);
		}
		return p;
	}

	private void init() {
		final File base = FolderContentProvider.ZIP_BASE;
		if (!base.exists()) {
			base.mkdirs();
		}
		final File imp = FolderContentProvider.ZIP_IMPORT;
		if (!imp.exists()) {
			imp.mkdirs();
		}
	}

	private void initKey(Bundle b) {
		if (b != null) {
			try {
				String[] sa = b.getStringArray(SAVED_KEY);
				if (sa != null) {
					Key key = CryptoUtils.deserializeKey(sa);
					KeyStore.setKey(key);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void initLicense() {
		// admob
		try {
			boolean noezb = (License.isEZBoardingAvailable(this) == false);

			AdView banner = (AdView) findViewById(R.id.banner);
			banner.setEnabled(noezb);
			banner.setVisibility(noezb ? View.VISIBLE : View.GONE);

			if (Debug.DEBUG) {
				Log.d(TAG, "No ezb, ads enabled? " + noezb);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean isZipEncrypted(int pos) {
		Cursor c = cursor;
		c.moveToPosition(pos);
		String s = c.getString(c.getColumnIndexOrThrow(Columns.ENCRYPTED));
		return Boolean.parseBoolean(s);
	}

	private void lockZip(int position) throws Exception {
		File enc = null;
		try {
			String p = getPath(position);
			if (p == null || !p.endsWith(".zip")) {
				return;
			}
			File file = new File(p);
			String fn = p + ".enc";
			enc = new File(fn);
			if (Debug.DEBUG) {
				Log.d(TAG, " file: " + file + " enc: " + enc);
			}
			//
			cleanupZipCache(file.getName());
			cleanupZipCache(enc.getName());
			// 
			Key key = KeyStore.getKey();
			CryptoUtils.encrypt(file, enc, key);
			//
			file.delete();
		} catch (Exception e) {
			if (enc != null) {
				enc.delete();
			}
			throw e;
		} finally {
			//
		}
	}

	/* pkg */void login(String pwd) throws Exception {
		final Context ctx = this;
		boolean login = false;
		if (TextUtils.isEmpty(pwd)) {
			login = false;
		} else {
			String hash = Preferences.getLockHash(ctx);
			String d = CryptoUtils.digest(pwd);
			login = d.equals(hash);
		}
		if (login == false) {
			Preferences.setLoggedIn(ctx, false);
			KeyStore.setKey(null);
			//
			Toast.makeText(ctx, R.string.msg_pwd_invalid, Toast.LENGTH_SHORT)
					.show();
		} else {
			Preferences.setLoggedIn(ctx, true);
			String[] ka = Preferences.getLockKey(this);
			ka[0] = pwd;
			Key key = CryptoUtils.unwrap(ka);
			KeyStore.setKey(key);
			//
			Toast.makeText(ctx, R.string.msg_login_ok, Toast.LENGTH_SHORT)
					.show();
			//
			refresh();
		}
		// update
		Notifications.addNotification(ctx);
	}

	protected void logout(boolean quit) {
		final Context ctx = this;
		Preferences.setLoggedIn(ctx, false);
		KeyStore.setKey(null);
		//
		clearCache();
		//
		if (quit) {
			Notifications.removeNotification(ctx);
		} else {
			Notifications.addNotification(ctx);
		}
		//
		Toast.makeText(ctx, R.string.msg_logged_out, Toast.LENGTH_SHORT).show();
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onActivityResult requestCode: " + requestCode
					+ " resultCode:" + resultCode + " == " + RESULT_OK
					+ " data: " + data);
		}
		String act = getIntent().getAction();
		if (requestCode == RC_MULTI_SELECT
				&& (Intent.ACTION_GET_CONTENT.equals(act) || Intent.ACTION_PICK
						.equals(act))) {
			setResult(resultCode, data);
			finish();
		}
		if (resultCode == RESULT_OK) {
			switch (requestCode) {
			case RC_IMPORT:
				refresh();
				break;
			case RC_SETTINGS:
				// final Context ctx = this;
				// if (Preferences.enableStatusbar(ctx)) {
				// Notifications.addNotification(ctx);
				// } else {
				// Notifications.removeNotification(ctx);
				// }
				break;
			}
		}
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		AdapterView.AdapterContextMenuInfo cmi = null;
		try {
			cmi = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
			//
			int pos = cmi.position;
			int id = item.getItemId();

			switch (id) {
			case R.id.main_lock_context_menu_id:
				doLock(pos);
				break;
			case R.id.main_unlock_context_menu_id:
				doUnlock(pos);
				break;
			case R.id.main_open_context_menu_id:
				doOpen(pos);
				break;
			case R.id.main_delete_context_menu_id:
				doDelete(pos);
			default:
			}
			return true;
		} catch (ClassCastException e) {
			Log.e(TAG, "menuInfo", e);
		} catch (Exception e) {
			Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreate " + savedInstanceState);
		}
		Eula.show(this);
		//
		// requestWindowFeature(Window.FEATURE_LEFT_ICON);
		requestWindowFeature(Window.FEATURE_NO_TITLE);

		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// setTitle(R.string.app_name);
		// getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
		// R.drawable.icon25);
		// 
		//
		ListView lv = getListView();
		lv.setFastScrollEnabled(false);
		registerForContextMenu(lv);
		//
		init();
		//
		initLicense();
		//
		initKey(savedInstanceState);
		//
		InitTask task = new InitTask();
		task.execute();

		if (Debug.DEBUG) {
			Log.d(TAG, "end onCreate " + savedInstanceState);
		}
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.main_context, menu);
		//
		AdapterView.AdapterContextMenuInfo cmi = null;
		try {
			cmi = (AdapterView.AdapterContextMenuInfo) menuInfo;
		} catch (ClassCastException e) {
			Log.e(TAG, "menuInfo", e);
			return;
		}
		int pos = cmi.position;
		//
		MenuItem lock = menu.findItem(R.id.main_lock_context_menu_id);
		MenuItem unlock = menu.findItem(R.id.main_unlock_context_menu_id);
		// toggle lock/unlock
		boolean enc = isZipEncrypted(pos);
		boolean login = Preferences.isLoggedIn(this);
		lock.setVisible(!enc && login);
		unlock.setVisible(enc && login);
		//
		lock.setEnabled(login);
		unlock.setEnabled(login);
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_HELP:
			return Dialogs.createHelpDialog(this);
		case DIALOG_LOGIN:
			return Dialogs.createLoginDialog(this);
		case DIALOG_SETPWD:
			return Dialogs.createSetPasswordDialog(this);
		case DIALOG_PWD:
			return Dialogs.createPasswordDialog(this);
		case DIALOG_PROGRESS:
			return Dialogs.createProgressDialog(this);
		case DIALOG_DELETE:
			diaDelete = Dialogs.createDeleteDialog(this);
			return diaDelete;
		default:
		}
		return null;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreateOptionsMenu: " + menu);
		}
		//
		MenuInflater mif = getMenuInflater();
		mif.inflate(R.menu.main_options, menu);
		miLogin = menu.findItem(R.id.main_login_opt_menu_id);
		miLogout = menu.findItem(R.id.main_logout_opt_menu_id);
		miSetPwd = menu.findItem(R.id.main_setpass_opt_menu_id);
		miChangePwd = menu.findItem(R.id.main_changepass_opt_menu_id);
		miQuit = menu.findItem(R.id.main_quit_opt_menu_id);
		return true;
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		doOpen(position);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onOptionsItemSelected: " + item);
		}
		int id = item.getItemId();
		switch (id) {
		case R.id.main_login_opt_menu_id:
			doLogin();
			return true;
		case R.id.main_logout_opt_menu_id:
			doLogout();
			return true;
		case R.id.main_find_opt_menu_id:
			doFindZip();
			return true;
		case R.id.main_help_opt_menu_id:
			showDialog(DIALOG_HELP);
			return true;
		case R.id.main_settings_opt_menu_id:
			doSettings();
			return true;
		case R.id.main_quit_opt_menu_id:
			doQuit();
			return true;
		case R.id.main_setpass_opt_menu_id:
			showDialog(DIALOG_SETPWD);
			return true;
		case R.id.main_changepass_opt_menu_id:
			showDialog(DIALOG_PWD);
			return true;
		default:
		}
		return false;
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		switch (id) {
		case DIALOG_DELETE:
			String name = getName(selPosition);
			diaDelete.setTitle(getString(R.string.text_delete, name));
			break;
		default:
		}
		super.onPrepareDialog(id, dialog);
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		boolean set = (!TextUtils.isEmpty(Preferences.getLockHash(this)));
		boolean login = Preferences.isLoggedIn(this);

		miSetPwd.setVisible(!set);
		// toggle log in/out
		miLogin.setVisible(set && !login);
		miLogout.setVisible(set && login);

		miChangePwd.setVisible(set && login);
		return true;
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		//
		saveKey(outState);
	}

	private void saveKey(Bundle b) {
		try {
			Key key = KeyStore.getKey();
			if (key != null) {
				String[] sa = CryptoUtils.serializeKey(key);
				b.putStringArray(SAVED_KEY, sa);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void refresh() {
		if (Debug.DEBUG) {
			Log.d(TAG, "freshing...");
		}
		cursor.requery();
		getListView().invalidate();
	}

	private void setAdapter() {
		final Context ctx = this;
		ListAdapter adapter = new SimpleCursorAdapter(ctx, R.layout.list_item2,
				cursor, new String[] { Columns.ICON_RESOURCE, Columns.NAME,
						Columns.DESCRIPTION }, new int[] { R.id.icon,
						R.id.text1, R.id.text2 });
		setListAdapter(adapter);
	}

	private void showError(Exception e) {
		Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
	}

	private void unlockZip(int position) throws Exception {
		File file = null;
		try {
			String p = getPath(position);
			//
			if (p == null || !p.endsWith(".enc")) {
				return;
			}
			File enc = new File(p);
			int len = p.length();
			String fn = p.substring(0, len - 4);
			file = new File(fn);
			if (Debug.DEBUG) {
				Log.d(TAG, " enc: " + enc + " file: " + file);
			}
			//
			cleanupZipCache(file.getName());
			cleanupZipCache(enc.getName());
			// 
			Key key = KeyStore.getKey();
			CryptoUtils.decrypt(enc, file, key);
			//
			enc.delete();
			//
		} catch (Exception e) {
			if (file != null) {
				file.delete();
			}
			throw e;
		} finally {
			//
		}
	}

}
